import weakref
import pyglet
from pyglet.image import Animation
from pyglet.sprite import Sprite
from pyglet.gl import *
from pymunk import inf, Vec2d, Body, Circle, Poly
from util import *

class GameObject(object):
    def draw(self):
        pass
    def draw_front(self):
        pass
    def get_body_and_shapes(self):
        return []
    def update(self, dt):
        pass
    def destroy(self):
        pass

class Lever(GameObject):
    def __init__(self, game_map, x, y, action = None):
        self.position = (x,y)
        self.sprite_off = make_sprite('graphics/lever_off.png', 47, 8)
        self.sprite_on = make_sprite('graphics/lever_on.png', 47, 16)
        self.sprite = self.sprite_off
        self.sprite.position = self.position

        self.map = weakref.proxy(game_map)
        self.shape = Poly(self.map.static_body, [(x-20,y), (x+20,y), (x+20,y+25), (x-20,y+25)])
        self.shape.sensor = True
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 3
        self.map.space.add_static(self.shape)

        self.state = 'off'

        self.action = action

    def draw(self):
        self.sprite.draw()

    def switch_on(self):
        if self.state == 'on':
            return
        self.sprite = self.sprite_on
        self.sprite.position = self.position
        self.state = 'on'
        if self.action:
            self.action()
            
    def destroy(self):
        del self.shape
        del self.map

class Ladder(GameObject):
    def __init__(self, game_map, x, y, state=None):
        self.position = Vec2d(x,y)
        self.sprite_long = make_sprite('graphics/ladder.png')
        self.sprite_short = make_sprite('graphics/ladder_short.png')

        self.map = weakref.proxy(game_map)
        
        if not state: state='short'
        
        self.change_state(state)

    def change_state(self, state):
        self.state = state
        if state == 'short':
            self.sprite = self.sprite_short
            height_diff = self.sprite_long.height - self.sprite_short.height
            self.sprite.position = self.position + Vec2d(-0.5*self.sprite.width, height_diff)
            self.add_static_shape()
        elif state == 'long':
            self.sprite = self.sprite_long
            self.sprite.position = self.position + Vec2d(-0.5*self.sprite.width, 0)
            self.add_static_shape()
        
    def add_static_shape(self):
        if hasattr(self, 'shape') and self.shape:
            self.map.space.remove_static(self.shape)
        x1, y1 = self.sprite.position
        x2, y2 = self.sprite.position + Vec2d(self.sprite.width, self.sprite.height)
        self.top = y2
        vertices = [(x1,y1), (x2,y1), (x2,y2), (x1,y2)]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.sensor = True
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 3
        self.map.space.add_static(self.shape)        

    def extend(self):
        if self.state == 'long':
            return
        self.change_state('long')

    def draw(self):
        self.sprite.draw()

    def destroy(self):
        del self.shape
        del self.map

class TimeMachine(GameObject):
    def __init__(self, game_map, x, y, baby_capacity=9):
        self.position = Vec2d(x,y)
        self.baby_capacity = baby_capacity # how many babies can fit inside?
        self.sprite = make_sprite('graphics/time_machine.png',188,40)
        self.sprite_front = make_sprite('graphics/time_machine_front.png', 122, 1)
        self.sprite.position = self.position
        self.sprite_front.position = self.position
        self.map = weakref.proxy(game_map)

        vertices = [(-70,0), (-118,0), (-185,230), (-70,240)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape1 = Poly(self.map.static_body, vertices)
        self.shape1.object = weakref.ref(self)
        self.shape1.collision_type = 0
        self.map.space.add_static(self.shape1)

        vertices = [(123,0), (96,0), (96,250), (185,240)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape2 = Poly(self.map.static_body, vertices)
        self.shape2.object = weakref.ref(self)
        self.shape2.collision_type = 0
        self.map.space.add_static(self.shape2)

        vertices = [(-70,0), (-70,20), (96,20), (96,0)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape3 = Poly(self.map.static_body, vertices)
        self.shape3.object = weakref.ref(self)
        self.shape3.sensor = True
        self.shape3.collision_type = 3
        self.map.space.add_static(self.shape3)

        self.babies = []

    def add_baby(self, baby):
        if len(self.babies) < self.baby_capacity:
            self.babies.append(baby)
        else:
            # Time machine is full?  Sorry little baby,
            # you will be lost in the cracks of time.
            pass

    def draw(self):
        self.sprite.draw()

    def draw_front(self):
        self.sprite_front.draw()

    def destroy(self):
        del self.shape1
        del self.shape2
        del self.shape3
        del self.map
        del self.babies


class BigHead(GameObject):
    def __init__(self, game_map, x, y):
        self.position = Vec2d(x,y)
        self.sprite_top = make_sprite('graphics/bighead_top.png',218,75)
        self.sprite_bottom = make_sprite('graphics/bighead_bottom.png', 215, 43)
        self.sprite_bottom.position = self.position
        self.sprite_top.position = self.position + Vec2d(0,150)
        self.state = 'half-open'

        self.map = weakref.proxy(game_map)

        self.add_static_shape()

    def add_static_shape(self):
        vertices = [(-180,0), (-180,320), (-80,450), (80, 450), (180,320), (180,0)]
        vertices = [self.sprite_top.position + Vec2d(v) for v in vertices]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 0
        self.map.space.add_static(self.shape)
        
    def draw_front(self):
        self.sprite_top.draw()
        self.sprite_bottom.draw()
        
    def destroy(self):
        del self.shape
        del self.map

    def update(self, dt):
        if self.state == 'opening':
            if self.sprite_top.y - self.sprite_bottom.y < 300:
                self.sprite_top.y += 20*dt
                self.map.space.remove_static(self.shape)
                self.add_static_shape()
            else:
                self.state = 'open'

    def open_mouth(self):
        self.state = 'opening'

class BabyGlue(GameObject):
    """Used to stop babies in their tracks."""
    def __init__(self, game_map, x, y):
        self.position = Vec2d(x, y)
        self.map = weakref.proxy(game_map)
        
        vertices = [(-35,0), (-35,10), (35,10), (35,0)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.object = weakref.ref(self)
        self.shape.sensor = True
        self.shape.collision_type = 3
        self.map.space.add_static(self.shape)

    def destroy(self):
        del self.shape
        del self.map
    
    
class Boulders(GameObject):
    """Used to block off the time cave."""
    def __init__(self, game_map, x, y):
        self.position = Vec2d(x, y)
        self.sprite = make_sprite('graphics/boulders.png',0,0)
        self.sprite.position = self.position
        self.map = weakref.proxy(game_map)

        vertices = [(42,24), (42,445), (436,445), (436,24)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 0
        self.map.space.add_static(self.shape)

    def draw(self):
        self.sprite.draw()

    def destroy(self):
        del self.shape
        del self.map

class MushroomDoor(GameObject):
    def __init__(self, game_map, x, y):
        self.position = Vec2d(x, y)
        self.sprite_closed = make_sprite('graphics/mushroom_door_closed.png',168,74)
        self.sprite_open = make_sprite('graphics/mushroom_door_open.png',163,71)
        self.sprite_front = make_sprite('graphics/mushroom_door_front.png',68,20)
        self.sprite = self.sprite_closed
        self.sprite.position = self.position
        self.sprite_front.position = self.position
        self.state = 'closed'

        self.map = weakref.proxy(game_map)
        
        vertices = [(-90,-30),(-90,330),(90,330),(90,-30)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 0
        self.map.space.add_static(self.shape)

    def draw(self):
        self.sprite.draw()

    def draw_front(self):
        if self.state == 'open':
            self.sprite_front.draw()
        
    def open_door(self):
        if self.state == 'open': return
        self.state = 'open'
        self.sprite = self.sprite_open
        self.sprite.position = self.position
        self.map.space.remove_static(self.shape)

        vertices = [(-90,200),(-90,330),(90,330),(90,200)]
        vertices = [self.position + Vec2d(v) for v in vertices]
        self.shape = Poly(self.map.static_body, vertices)
        self.shape.object = weakref.ref(self)
        self.shape.collision_type = 0
        self.map.space.add_static(self.shape)
        

    def destroy(self):
        del self.shape
        del self.map


class WeakBridge(GameObject):
    def __init__(self, game_map, x, y):
        self.position = Vec2d(x, y)
        self.sprite1 = make_sprite('graphics/bridge1.png')
        self.sprite2 = make_sprite('graphics/bridge2.png')
        self.sprite3 = make_sprite('graphics/bridge3.png')
        self.sprite4 = make_sprite('graphics/bridge4.png')

        self.map = weakref.proxy(game_map)

        self.change_state('normal')

        self.objects = weakref.WeakKeyDictionary()

    def add_object(self, obj):
        self.objects[obj] = True
        weight = self.supported_weight()
        if self.state == 'normal' and weight >= 110:
            self.change_state_post_step('cracked')
        if self.state == 'cracked' and weight >= 140:
            self.change_state_post_step('really-cracked')
        if self.state == 'really-cracked' and weight >= 160:
            self.change_state_post_step('broken')

    def change_state_post_step(self, state):
        def callback(obj):
            obj.change_state(state)
        self.map.space.add_post_step_callback(callback, self)

    def supported_weight(self):
        weight = 0
        for obj in self.objects:
            weight += obj.weight()
        return weight

    def remove_object(self, obj):
        del self.objects[obj]
    
    def draw(self):
        self.sprite.draw()

    def destroy(self):
        if hasattr(self, 'shape1'): del self.shape1
        if hasattr(self, 'shape2'): del self.shape2
        del self.map

    def change_state(self, state):
        self.state = state
        if hasattr(self, 'shape1'):
            self.map.space.remove_static(self.shape1)
            del self.shape1
        if hasattr(self, 'shape2'):
            self.map.space.remove_static(self.shape1)
            del self.shape2
            
        sprites = {'normal':self.sprite1,'cracked':self.sprite2,
                   'really-cracked':self.sprite3,'broken':self.sprite4}
        self.sprite = sprites[state]
        self.sprite.position = self.position

        if state == 'broken':
            vertices = [(-10,0),(-10,116),(253,116),(253,0)]
            vertices = [self.position + Vec2d(v) for v in vertices]
            self.shape1 = Poly(self.map.static_body, vertices)
            self.shape1.object = weakref.ref(self)
            self.shape1.collision_type = 4
            self.map.space.add_static(self.shape1)

            vertices = [(420,0),(420,116),(710,116),(710,0)]
            vertices = [self.position + Vec2d(v) for v in vertices]
            self.shape2 = Poly(self.map.static_body, vertices)
            self.shape2.object = weakref.ref(self)
            self.shape2.collision_type = 4
            self.map.space.add_static(self.shape2)
        else:
            vertices = [(-10,0),(-10,116),(720,116),(720,0)]
            vertices = [self.position + Vec2d(v) for v in vertices]
            self.shape1 = Poly(self.map.static_body, vertices)
            self.shape1.object = weakref.ref(self)
            self.shape1.collision_type = 4
            self.map.space.add_static(self.shape1)

